#include "ConstruitRace.h"

#include "Race.h"

#include "ATL.h"

#include <iomanip>

#include "Qlib.h"

#include <QtGui/QVBoxLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QGroupBox>
#include <QtGui/QPushButton>
#include <QtGui/QLCDNumber>
#include <QtGui/QCheckBox>
#include <QtGui/QButtonGroup>
#include <QtGui/QPushButton>
#include <QtGui/QGridLayout>

#include <QtGui/QApplication>

using namespace std;

DebugLevelType ConstruitRace::gestionListe = BLANK;

/**
 * Constructeur par défaut. Récupère l'ensembles des capacités raciales
 * et éventuels ensembles raciaux, et permet de construire la race.
 *
 * @param[in] race pointeur sur la Race.
 * @param[in] parent pointeur sur le widget parent (orphelin par défaut).
 */
ConstruitRace::ConstruitRace( Race *race, QWidget* parent )
  : BaseUI( "ConstruitRace", parent), ensPris( false )
{
  mRace = race;

  boite = new QGroupBox( trUtf8("Choix des capacités :"), this);
  boiteEns = 0;

  nbrPP = new QLCDNumber(this);
  nbrPP -> setSegmentStyle(QLCDNumber::Flat);

  preparerAffichage();

  boutons.push_back( new QPushButton( trUtf8("&Quitter"), this ) );
  boutons.back() -> setToolTip( trUtf8("Quitter le programme") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( annuler() ) );
  boutons.push_back( new QPushButton( trUtf8("&Update"), this ) );
  boutons.back() -> setToolTip( trUtf8("Mettre à jour l'affichage, càd\nappliquer l'ensemble racial sélectionné") );
  if ( ens == 0 )
    boutons.back() -> setEnabled( false );
  connect( boutons.back() , SIGNAL( clicked() ), this, SLOT( miseAJour() ) );
  boutons.push_back( new QPushButton( trUtf8("&OK"), this ) );
  boutons.back() -> setToolTip( trUtf8("Accepter et continuer") );
  connect( boutons.back(), SIGNAL( clicked() ), this, SLOT( fermer() ) );

  fenetreLayout -> addWidget(nbrPP);
  fenetreLayout -> addWidget(boite);
  if (boiteEns != 0)
    fenetreLayout -> addWidget(boiteEns);

  setFenetreLayout();
}

/**
 * Destructeur, ne fait rien de spécial.
 */
ConstruitRace::~ConstruitRace()
{}

/**
 * Effectue le choix des compétences raciales. Affiche le widget et
 * permet la sélection des capacités (avec ou sans ensemble).
 *
 * @return StatusCode \e true si choix validé, \e false sinon, le nombre de
 * PP restant est donné par valeur().
 */
StatusCode ConstruitRace::raceChoisie()
{
  this -> show();

  qApp -> exec();

  return StatusCode(OK, PP);
}

/**
 * Permet de modifier la valeur de ConstruitRace::gestionListe.
 *
 * @param[in] level nouvelle valeur de ConstruitRace::gestionListe.
 */
void ConstruitRace::setLevel( const DebugLevelType& level, const unsigned& )
{
  gestionListe = level;
}

/**
 * Génère le contenu de la fenêtre. On lit les capacités, puis les
 * éventuels ensembles raciaux. Les boutons sont créés et placés dans
 * le QGroupBox correspondant.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche la progression des actions effectuées.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche la correspondance numéro de bouton - nom, ainsi
 * que le nombre d'ensembles, si celui-ci est non-nul.
 */
void ConstruitRace::preparerAffichage()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitRace::preparerAffichage -> début" << endmsg;

  PP = mRace -> pp();

  nbrPP -> display(static_cast<int>(PP));

  mRace -> copieCapacitesRaciales(nbrCapa, Capacites);

  info() << left << setw(22) << ecrireString( nbrCapa ) + " capacités"
	 << " [ OK ]" << endmsg;

  aCapa.assign(nbrCapa, false);

  mRace -> donneesEnsRaciaux(ens, maxCapa);

  info() << left << setw(22) << ecrireString( ens ) +  " ensembles raciaux"
	 << " [ OK ]" << endmsg;

  aEns.assign(ens, false);

  QGridLayout *boiteLayout = new QGridLayout(boite);

  groupe = new QButtonGroup(this);

  groupe -> setExclusive ( false );

  for (unsigned int i(0); i < nbrCapa; i++)
    {
      groupe -> addButton(new QCheckBox( trUtf8(Capacites[i].nom().c_str()),
					 this), i);
      debug() << i << " -> "
	      << toUtf8StdString( groupe -> button( i ) -> text() ) << endmsg;
      boiteLayout -> addWidget(groupe -> button(i), i % 10, i / 10 );
    }

  info() << "Boutons des capacités [ OK ]" << endmsg;

  boite -> setLayout(boiteLayout);

  if (ens > 0)
    {
      const vector< EnsembleCapacitesRaciales > *listeEnsembles;
      listeEnsembles = mRace -> ensemblesRaciaux();
      info() << "Ensembles raciaux détectés" << endmsg;
      boiteEns = new QGroupBox( trUtf8("Ensembles raciaux :"), this );

      QGridLayout *boiteEnsLayout = new QGridLayout(boiteEns);
      debug() << "Nombre d'ensembles : " << listeEnsembles -> size()
	      << endmsg;

      for (unsigned int i(nbrCapa), j(0); j < ens; i++, j++)
	{
	  groupe -> addButton(new QCheckBox( trUtf8( (listeEnsembles -> at( j )).nom().c_str()), this), i);
	  Capacites.push_back( CapaciteRaciale( listeEnsembles -> at( j ).nom(),
						listeEnsembles -> at( j ).prix() ) );
			       
	  debug() << i << " -> "
		  << toUtf8StdString( groupe -> button( i ) -> text() )
		  << endmsg;
	  boiteEnsLayout -> addWidget(groupe -> button( i ), j % 3, j / 3 );
	}      
      boiteEns -> setLayout(boiteEnsLayout);
    }

  info() << "Boutons des ensembles [ OK ]" << endmsg;

  connect(groupe, SIGNAL(buttonClicked(int)), this, SLOT(ajouterCapacite(int)));

  func() << "ConstruitRace::preparerAffichage -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Permet d'ajouter une compétence (ou de sélectionner un
 * ensemble). Lors d'une sélection quelconque, le coût est
 * vérifié. Dans tous les cas le nombre de PP est mis à jour sur
 * l'affichage.
 *
 * Lorsqu'un ensemble racial a élé sélectionnée, il devient impossible
 * de modifier les capacités individuellement.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche le nom de la capacité (ou ensemble) ajoutée ou
 * supprimée.
 */
void ConstruitRace::ajouterCapacite(const int& cap)
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitRace::ajouterCapacite -> début" << endmsg;

  if ( ensPris && cap < static_cast< int >( nbrCapa ) )
    {
      /*
       * Sert à  ignorer tout changement aux capacités raciales tant
       * qu'un ensemble racial est sélectionné.
       */
      warning() << "Ensemble racial sélectionné, impossible de modifier "
		<< "la sélection des capacités" << endmsg;
      groupe -> button(cap)
	-> setChecked( ! groupe -> button(cap) -> isChecked() );

      func() << "ConstruitRace::ajouterCapacite -> fin" << endmsg;
      /*Debug::*/depileNiveau();
      return;
    }

  if (groupe -> button(cap) -> isChecked())
    {
      if ( PP < Capacites[cap].prix() )
	{
	  warning() << "Nombre de PP insuffisant : " << PP
		    << ", coût : " << Capacites[cap].prix() << endmsg;
	  groupe -> button(cap) -> setChecked( false );

	  func() << "ConstruitRace::ajouterCapacite -> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return;
	}
      debug() << "Sélection de " << Capacites[cap].nom() << endmsg;
      PP -= Capacites[cap].prix();
      aCapa[cap] = true;
    }
  else
    {
      debug() << "Suppression de " << Capacites[cap].nom() << endmsg;
      aCapa[cap] = false;
      PP += Capacites[cap].prix();
    }

  nbrPP -> display(static_cast<int>(PP));

  func() << "ConstruitRace::ajouterCapacite -> fin" << endmsg;
  /*Debug::*/depileNiveau();//dout.setLevel( etat_precedent );
}

/**
 * La méthode fermer() est redéfinie pour vérifier que l'on ne garde
 * pas plus de 5 PP pour la phase suivante (qui est la création de la
 * Classe), sauf dans le cas d'un humain. La méthode modifie en outre
 * la Race.
 */
void ConstruitRace::fermer()
{
  if ( PP > 5 && mRace -> type() != "Humain" )
    {
      warning() << "Impossible de garder plus de 5 pp pour la suite" << endmsg;
      return;
    }

  OK = true;
    
  vector<unsigned int> vec;

  for (unsigned int i(0); i < (nbrCapa - ens); i++)
    {
      if (aCapa[i])
	vec.push_back(i);
    }

  mRace -> setCapacites(vec);

  close();
}

/**
 * Cette méthode sert à appliquer les changements dus à un ensemble
 * racial : la liste des capacités de l'ensemble est lue puis
 * appliquée.
 */
void ConstruitRace::miseAJour()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "ConstruitRace::miseAJour -> début" << endmsg;

  unsigned int ensChoisi, j, index;

  for (unsigned int i(nbrCapa); i < nbrCapa + ens; i++)
    {
      if ( groupe -> button( static_cast<int>( i ) ) -> isChecked() )
	{
	  debug() << "Sélection de l'ensemble racial "
		  << toUtf8StdString( groupe -> button( static_cast<int>( i ) )
				      -> text() ) << endmsg;
	  aEns[i] = true;
	  ensPris = true;

	  ensChoisi = i - nbrCapa;

	  ensRacial = &( mRace -> ensemblesRaciaux() -> at( ensChoisi ) . 
			 membres() );
	  aCapa.assign(nbrCapa, false);

	  maxCapa = ensRacial -> size();

	  for (j = 0; j < maxCapa; j++)
	    {
	      index = ensRacial -> at( j ) - 1;
	      groupe -> button( static_cast<int>( index ) ) -> setChecked( true );
	      aCapa[index] = true;
	      debug() << "Ajout de "
		      << toUtf8StdString( groupe -> button( static_cast<int>( index ) )
					  -> text() ) << endmsg;
	      
	    }
	  func() << "ConstruitRace::miseAJour -> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return;
	}
      else if ( aEns[i] &&
		! (groupe -> button( static_cast<int>( i ) ) -> isChecked() ) )
	{
	  debug() << "Suppression de l'ensemble racial "
		  << toUtf8StdString( groupe -> button(static_cast<int>(i))
				      -> text() ) << endmsg;
	  
	  aEns[i] = false;
	  ensPris = false;

	  ensChoisi = i - nbrCapa;

	  ensRacial = &( mRace -> ensemblesRaciaux() -> at( ensChoisi ) .
			 membres() );
	  aCapa.assign(nbrCapa, false);

	  maxCapa = ensRacial -> size();

	  for (j = 0; j < maxCapa; j++)
	    {
	      groupe -> button(static_cast<int>(ensRacial -> at( j ) - 1))
		-> setChecked( false );
	    }
	  func() << "ConstruitRace::miseAJour -> fin" << endmsg;
	  /*Debug::*/depileNiveau();
	  return;
	}
    }
}
